package ex.action.map.shape;

import java.io.Serializable;
import java.util.Iterator;
import java.util.List;


import com.egghiro.framework.j2se.game.model.shape.Matrix2D;

import ex.action.map.Field2D;

public class Vector2D implements Serializable {

    protected double x, y;

    public Vector2D(double value) {
        this(value, value);
    }

    public Vector2D() {
        this(0, 0);
    }

    public Vector2D(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public Vector2D(Vector2D vector2D) {
        this.x = vector2D.x;
        this.y = vector2D.y;
    }

    public void move(Vector2D vector2D) {
        this.x += vector2D.x;
        this.y += vector2D.y;
    }

    public void move_multiples(int direction, int multiples) {
        if (multiples <= 0) {
            multiples = 1;
        }
        Vector2D v = Field2D.getDirection(direction);
        move(v.x() * multiples, v.y() * multiples);
    }

    public void move(double x, double y) {
        this.x += x;
        this.y += y;
    }

    public double[] getCoords() {
        return (new double[] { x, y });
    }

    public void setLocation(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public boolean equals(Object o) {
        if (o instanceof Vector2D) {
            Vector2D p = (Vector2D) o;
            return p.x == x && p.y == y;
        }
        return false;
    }

    public int hashCode() {
        return (int) (x + y);
    }

    public void setX(double x) {
        this.x = x;
    }

    public void setY(double y) {
        this.y = y;
    }

    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }

    public int x() {
        return (int) x;
    }

    public int y() {
        return (int) y;
    }

    public Object clone() {
        return new Vector2D(x, y);
    }

    public void set(Vector2D other) {
        set(other.getX(), other.getY());
    }

    public void set(float x, float y) {
        this.x = x;
        this.y = y;
    }

    public void set(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public Vector2D reverse() {
        x = -x;
        y = -y;
        return this;
    }

    public float length() {
        return (float) Math.sqrt(x * x + y * y);
    }

    public double lengthSquared() {
        return (x * x) + (y * y);
    }

    public Vector2D add(Vector2D other) {
        double x = this.x + other.x;
        double y = this.y + other.y;
        return new Vector2D(x, y);
    }

    public Vector2D addThis(Vector2D other) {
        this.x += other.x;
        this.y += other.y;
        return this;
    }

    public static Vector2D sum(List summands) {
        Vector2D result = new Vector2D(0, 0);
        for (Iterator it = summands.iterator(); it.hasNext();) {
            Vector2D v = (Vector2D) it.next();
            result.addThis(v);
        }
        return result;
    }

    public static Vector2D sum(Vector2D a, Vector2D b) {
        Vector2D answer = new Vector2D(a);
        return answer.addThis(b);
    }

    public static Vector2D mean(List points) {
        int n = points.size();
        if (n == 0) {
            return new Vector2D(0, 0);
        }
        return Vector2D.sum(points).scale(1.0 / n);

    }

    public Vector2D sub(Vector2D v) {
        x -= v.getX();
        y -= v.getY();
        return this;
    }

    public Vector2D subtract(Vector2D other) {
        double x = this.x - other.x;
        double y = this.y - other.y;
        return new Vector2D(x, y);
    }

    public double dot(Vector2D vec) {
        return (x * vec.x) + (y * vec.y);
    }

    public static double cross(Vector2D a, Vector2D b) {
        return a.cross(b);
    }

    public double cross(Vector2D vec) {
        return x * vec.y - y * vec.x;
    }

    public Vector2D multiply(double value) {
        return new Vector2D(value * x, value * y);
    }

    public double dotProduct(Vector2D other) {
        return other.x * x + other.y * y;
    }

    public Vector2D scale(double a) {
        x *= a;
        y *= a;
        return this;
    }

    public Vector2D normalize() {
        double magnitude = Math.sqrt(dotProduct(this));
        return new Vector2D(x / magnitude, y / magnitude);
    }

    public double level() {
        return Math.sqrt(dotProduct(this));
    }

    public double distanceSquared(Vector2D other) {
        double dx = other.getX() - getX();
        double dy = other.getY() - getY();

        return (dx * dx) + (dy * dy);
    }

    public double distance(Vector2D other) {
        return Math.sqrt(distanceSquared(other));
    }

    public Vector2D modulate(Vector2D other) {
        double x = this.x * other.x;
        double y = this.y * other.y;
        return new Vector2D(x, y);
    }

    public boolean equalsDelta(Vector2D other, double delta) {
        return (other.getX() - delta < x && other.getX() + delta > x
                && other.getY() - delta < y && other.getY() + delta > y);
    }

    public static Vector2D getRealCordinates(Vector2D relativeCoordinate,
            Vector2D centroid, double angularPosition) {
        Vector2D answer = (new Matrix2D(angularPosition)).multiply(
                relativeCoordinate).add(centroid);
        return answer;

    }

    public static Vector2D difference(Vector2D first, Vector2D second) {
        Vector2D answer = new Vector2D(first);
        return answer.sub(second);
    }

    public void rotate90() {
        setLocation(y, -x);
    }

    public static Vector2D rotate90(Vector2D vec) {
        return new Vector2D(-vec.y, vec.x);
    }

    public static Vector2D rotate90R(Vector2D vec) {
        return new Vector2D(vec.y, -vec.x);
    }

    public static double dot(Vector2D a, Vector2D b) {
        return a.dot(b);
    }

    public static double crossZ(Vector2D a, Vector2D b) {
        return a.x * b.y - a.y * b.x;
    }

    public static Vector2D mult(Vector2D vector, double scalar) {
        Vector2D answer = new Vector2D(vector);
        return answer.scale(scalar);
    }

    public String toString() {
        return (new StringBuffer("[Vector2D x:")).append(x).append(" y:")
                .append(y).append("]").toString();
    }
}
